package com.workday.gridsimulator

import akka.actor.Actor
import akka.actor.ActorRef
import akka.event.Logging

/**
 * The work scheduler actor.
 *
 * Adds to the Clocks agenda and when it receives a ping back ( signifying work arrival ) it submits the work to the grid.
 *
 * Lets the grid and the clock know when it has finished submitting work.
 *
 * @param clock the clock
 *
 * @param grid the grid
 *
 * @param schedulerSubmissionLimit the amount of work to submit before waiting for the grid to answer back and then carrying on with submissions
 *
 */
class WorkScheduler(val clockActor: ActorRef, val gridActor: ActorRef, val schedulerSubmissionLimit: Int) extends Actor {

  var work: Seq[Work] = null
  var currentWork: Work = null
  var currentTime: Double = 0.0
  var workSubmittedCount = 0

  var arrivalRate: Int = 0
  var arrivalRateChangedByUser = false
  var newArrivalTime: Double = 0.0

  val log = Logging(context.system.eventStream, self.path.toString())

  override def receive = {

    case SetSchedulerWork(work, initialArrivalRate) =>
      log.info("work set for scheduling")
      this.work = work
      arrivalRate = initialArrivalRate

    case Start(currentTime) =>
      workSubmittedCount = 0
      this.currentTime = currentTime
      log.info("starting")

      if (work != null) {
        if (!work.isEmpty) {
          currentWork = work.head
          work = work.tail
          log.info("sending hold msg to clock for work id " + currentWork.id + " at " + this.currentTime)
          clockActor ! Hold(currentTime + currentWork.arrivalTime, context.self)
        }
      }

    case Stop(currentTime) =>
      log.info("told to stop at " + currentTime)
      currentWork = null
      work = null
      this.currentTime = 0.0
      clockActor ! Finished(self)
      
    case Ping(currentTime) =>
      if (currentWork != null) {
        this.currentTime = currentTime
        log.info("submitted work id " + currentWork.id + " to grid at " + this.currentTime)
        gridActor ! WorkSubmission(this.currentTime, currentWork)
        workSubmittedCount += 1

        if (!work.isEmpty) {
          currentWork = work.head
          work = work.tail

          if (arrivalRateChangedByUser) currentWork.arrivalTime = newArrivalTime

          if (workSubmittedCount != schedulerSubmissionLimit) {
            log.info("sending hold msg to clock for work id " + currentWork.id + " at " + this.currentTime)
            clockActor ! Hold(currentTime + currentWork.arrivalTime, context.self)
          } else gridActor ! "TellMeWhenToSubmitMoreWork"
        } else {
          gridActor ! WorkArrivalFinished(this.currentTime)
          clockActor ! Finished(self)
          log.info("work scheduler finished, no more jobs to schedule as of  " + currentTime)
        }
      }

    case WorkingOnWhat =>
      sender ! currentWork

    case WorkRemaining =>
      sender ! work

    case UserSaidDecrementArrivalRate =>
      arrivalRateChangedByUser = true
      arrivalRate -= 1
      newArrivalTime = 60.0 / arrivalRate

    case UserSaidIncrementArrivalRate =>
      arrivalRateChangedByUser = true
      arrivalRate += 1
      newArrivalTime = 60.0 / arrivalRate

    case "SendMeMoreWork" =>
      workSubmittedCount = 0
      self ! Ping(this.currentTime)
      
    case "CurrentTime" =>
      sender ! this.currentTime

    case _ =>
      log.warning("unknown message received from " + sender)
  }

}